package utils

import (
	"log"
	"strings"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/api/watch"
	"gopkg.in/yaml.v2"
)

/*
 +----------------------------------------------------------------------
 + Title        : ConsulConfigCenter
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2022-01-06
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : Consul 配置中心
 +				: 文档地址: https://github.com/hashicorp/consul
 +				: 插件: github.com/hashicorp/consul/api
 +----------------------------------------------------------------------
*/

// 定义CONSUL配置中心结构体
type ConsulConfigCenter struct {
	client         *api.Client
	configFullPath string
	address        string
}

/**
 * 创建CONSUL配置中心
 *
 * @param string addrs ------------------------------------------------必传
 * @param string token ------------------------------------------------必传
 * @param string configFullPath ---------------------------------------必传,必须全路径
 * @return *ConsulConfigCenter, error
 * @author huwl
 */
func NewConsulConfigCenter(addrs, token, configFullPath string) (*ConsulConfigCenter, error) {
	// 初始化consul配置
	consulConfig := api.DefaultConfig()
	consulConfig.Address = addrs
	consulConfig.Token = token
	// consulConfig.PathPrefix = ""

	client, err := api.NewClient(consulConfig)
	if err != nil {
		return nil, err
	}

	return &ConsulConfigCenter{
		client:         client,
		configFullPath: configFullPath,
		address:        addrs,
	}, nil
}

/**
 * 获取配置数据
 *
 * @param any ConfigStruct --------------------------------------------必传
 * @author huwl
 */
func (cc *ConsulConfigCenter) GetConfigData(ConfigStruct any) {
	// 获取配置信息：key格式->/micro/config/test/mysql_config
	kv, _, err := cc.client.KV().Get(cc.configFullPath, nil)
	if err != nil {
		log.Fatalf("Failed to get config: %v", err)
	}

	// 解析配置方式一：*viper.Viper
	// 读取配置的方式：defaultConfig.getString("single.host")
	// 读取配置的方式：defaultConfig.getString("single.database")
	// defaultConfig := viper.New()
	// defaultConfig.SetConfigType("yaml")
	// err = defaultConfig.ReadConfig(bytes.NewBuffer(kv.Value))
	// if err != nil {
	// 	log.Fatalf("failed to read config: %v", err)
	// }

	// 解析配置方式二：结构体
	yaml.Unmarshal(kv.Value, ConfigStruct)

	// 监控配置
	go cc.WatchConfig(cc.configFullPath, ConfigStruct, cc.address)
}

/**
 * 监控配置
 *
 * @param string configName -------------------------------------------必传
 * @param any ConfigStruct --------------------------------------------必传
 * @param string consulAddress ----------------------------------------必传
 * @author huwl
 */
func (cc *ConsulConfigCenter) WatchConfig(configName string, ConfigStruct any, consulAddress string) {
	params := make(map[string]interface{})
	params["type"] = "key"
	params["key"] = configName

	w, err := watch.Parse(params)
	if err != nil {
		log.Fatalf("Failed to watch parse: %v", err)
	}

	// 用于跳过第一次触发的标志
	firstTime := true

	// 初始启动时，Handler 会立即触发一次（获取当前值）。
	// 因此需要使用添加标识跳过，由于里面有初始化连接操作
	// 避免重复初始化连接，需要跳过第一次触发
	w.Handler = func(u uint64, i interface{}) {
		if firstTime {
			// 第一次触发，跳过
			firstTime = false
			// log.Println("跳过初始启动的触发")
			return
		}

		kv := i.(*api.KVPair)

		// 解析配置方式一：*viper.Viper
		// 读取配置的方式：defaultConfig.getString("single.host")
		// 读取配置的方式：defaultConfig.getString("single.database")
		// hotconfig := viper.New()
		// hotconfig.SetConfigType("yaml")
		// err = hotconfig.ReadConfig(bytes.NewBuffer(kv.Value))
		// if err != nil {
		// 	log.Fatalf("failed to read config: %v", err)
		// }
		// defaultConfig = hotconfig

		// 解析配置方式二：结构体
		yaml.Unmarshal(kv.Value, ConfigStruct)
		log.Printf("consul watch config data: %v", ConfigStruct)

		// 在这里更新配置，比如重新初始化数据库连接等
		configPath := strings.Split(configName, "/")
		switch configPath[len(configPath)-1] {
		case RedisConfigCenter:
			// 重新初始化 redis 连接
			ResetRedisInstance()
		case MongoConfigCenter:
			CloseMongo()
		case EsConfigCenter:
			CloseElasticSearch()
		}
	}

	err = w.Run(consulAddress)
	if err != nil {
		log.Fatalf("Failed to watch consul: %v", err)
	}
}
